package perfume

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"mall/common/moneyx"
	"mall/common/resultx"
	// "mall/service/backend/common/utils/pointy"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type PerfumeFilterLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewPerfumeFilterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PerfumeFilterLogic {
	return &PerfumeFilterLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *PerfumeFilterLogic) PerfumeFilter(req *types.PerfumeFilterReq) (resp *types.PerfumeFilterResp, err error) {
	filter := strings.TrimSpace(req.Filter)

	// Search for the indexed document
	// Build the request body
	var buf bytes.Buffer
	var query map[string]any
	var mustConditions []map[string]any
	mustConditions = append(mustConditions, map[string]any{
		"term": map[string]any{
			"Sections.Slug": "perfumes",
		},
	})
	search := strings.TrimSpace(req.Search)
	if len(search) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"multi_match": map[string]any{
				"query":  search,
				"fields": []string{"Title^3", "Slug^3", "ContentHtml"},
			},
		})
	}

	if len(strings.TrimSpace(req.BrandIds)) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"terms": map[string]any{
				"Brand.Id": strings.Split(strings.TrimSpace(req.BrandIds), ","),
			},
		})
	}
	if len(strings.TrimSpace(req.NoseIds)) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"terms": map[string]any{
				"Nose.Id": strings.Split(strings.TrimSpace(req.NoseIds), ","),
			},
		})
	}
	if len(strings.TrimSpace(req.GroupIds)) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"terms": map[string]any{
				"Group.Id": strings.Split(strings.TrimSpace(req.GroupIds), ","),
			},
		})
	}
	if len(strings.TrimSpace(req.NoteIds)) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"terms": map[string]any{
				"Note.Id": strings.Split(strings.TrimSpace(req.NoteIds), ","),
			},
		})
	}
	if len(strings.TrimSpace(req.Seasons)) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"terms": map[string]any{
				"Season.Key": strings.Split(strings.TrimSpace(req.Seasons), ","),
			},
		})
	}
	if len(strings.TrimSpace(req.Genders)) > 0 {
		mustConditions = append(mustConditions, map[string]any{
			"terms": map[string]any{
				"Gender.Key": strings.Split(strings.TrimSpace(req.Genders), ","),
			},
		})
	}
	prefix := strings.TrimSpace(req.Prefix)
	if len(prefix) == 1 {
		prefix = strings.ToLower(prefix)
	}

	var page int64 = 1
	var pageSize int64
	if req.Current > 1 {
		page = req.Current
	}
	if req.PageSize > 0 { // 指定，检查最大值
		pageSize = req.PageSize
		if pageSize > l.svcCtx.Config.Query.MaxAmount {
			pageSize = l.svcCtx.Config.Query.MaxAmount
		}
	} else { // 不指定，使用默认值
		pageSize = l.svcCtx.Config.Query.DefaultAmount
	}

	if len(filter) == 0 {
		var sorter map[string]any
		// 1：价格从低到高；
		// 2：价格从高到低；
		// 3：字母顺序；
		// 4：分数；
		// 5：受欢迎程度；
		// 6：年份从低到高；
		// 7：年份从高到低；
		switch req.Sorter {
		case 1:
			sorter = map[string]any{
				"PriceMin": map[string]any{
					"order": "asc",
				},
			}
		case 2:
			sorter = map[string]any{
				"PriceMax": map[string]any{
					"order": "desc",
				},
			}
		case 3:
			sorter = map[string]any{
				"Uri": map[string]any{
					"order": "asc",
				},
			}
		case 4:
			sorter = map[string]any{
				"Rating": map[string]any{
					"order": "desc",
				},
			}
		case 5:
			sorter = map[string]any{
				"Popularity": map[string]any{
					"order": "desc",
				},
			}
		case 6:
			sorter = map[string]any{
				"Year": map[string]any{
					"order": "asc",
				},
			}
		case 7:
			sorter = map[string]any{
				"Year": map[string]any{
					"order": "desc",
				},
			}
		default:
			sorter = map[string]any{}
		}

		query = map[string]any{
			"query": map[string]any{
				"bool": map[string]any{
					"must": mustConditions,
				},
			},
			"sort": sorter,
			"_source": []string{"Id", "Title", "Slug", "Sections.Name", "Sections.Uri", "Media.Uid", "Media.Id", "Media.Url",
				"Meta.Key", "Meta.Value", "Relations.Id", "Relations.Title", "Relations.Slug", "Relations.Section", "Relations.GroupId",
				"Brand.Id", "Brand.Title", "Brand.Slug", "Nose.Id", "Nose.Title", "Nose.Slug", "Group.Id", "Group.Title", "Group.Slug",
				"Note.Id", "Note.Title", "Note.Slug", "Note.GroupId", "Season.Key", "Season.Value", "Gender.Key", "Gender.Value",
				"Year", "Rating",
			},
			"from": int((page-1)*pageSize),
			"size": int(pageSize),
		}
		// logrus.Info(fmt.Sprintf("query: %+v", query))
		// INFO[0006] query: map[_source:[Id Title Slug Sections.Name Sections.Uri Media.Uid Media.Id Media.Url Meta.Key Meta.Value Relations.Id Relations.Title Relations.Slug Relations.Section Relations.GroupId Brand.Id Brand.Title Brand.Slug Nose.Id Nose.Title Nose.Slug Group.Id Group.Title Group.Slug Note.Id Note.Title Note.Slug Note.GroupId Season.Key Season.Value Gender.Key Gender.Value Year Rating] from:0 query:map[bool:map[must:[map[term:map[Sections.Slug:perfumes]] map[terms:map[Brand.Id:[42412]]]]]] size:10 sort:map[Popularity:map[order:desc]]]
		if err := json.NewEncoder(&buf).Encode(query); err != nil {
			logrus.Info(fmt.Sprintf("PerfumeFilter Error encoding query: %+v", err))
			return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
		}

		// Perform the search request.
		client := l.svcCtx.EsClient
		res, err := client.Search(
			client.Search.WithContext(l.ctx),
			client.Search.WithIndex("scentrobe-kb-entry"),
			client.Search.WithBody(&buf),
			client.Search.WithTrackTotalHits(true),
			client.Search.WithPretty(),
		)
		if err != nil {
			logrus.Info(fmt.Sprintf("Error getting response: %+v", err))
			return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
		}

		defer res.Body.Close()
		if res.IsError() {
			var e map[string]any
			if err = json.NewDecoder(res.Body).Decode(&e); err != nil {
				logrus.Info(fmt.Sprintf("Error parsing the response body: %+v", err))
			} else {
				// Print the response status and error information.
				logrus.Info(fmt.Sprintf(
					"[%s] %s: %s",
					res.Status(),
					e["error"].(map[string]any)["type"],
					e["error"].(map[string]any)["reason"],
				))
			}

			return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
		}

		// logrus.Info(fmt.Sprintf("PerfumeFilter res: %+v", res))

		var r map[string]any
		if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
			logrus.Info(fmt.Sprintf("Error parsing the response body: %+v", err))
			return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
		}

		var list []*types.PerfumeInfo
		var total int64
		total = int64(r["hits"].(map[string]any)["total"].(map[string]any)["value"].(float64))
		for _, hit := range r["hits"].(map[string]any)["hits"].([]any) {
			item := hit.(map[string]any)["_source"].(map[string]any)
			var sections []*types.ForumCategory
			if _, exist := item["Sections"]; exist {
				for _, section := range item["Sections"].([]any) {
					sections = append(sections, &types.ForumCategory{
						Name:  section.(map[string]any)["Name"].(string),
						Alias: section.(map[string]any)["Uri"].(string),
					})
				}
			}
			var media []*types.ForumMedia
			if _, exist := item["Media"]; exist {
				for _, m := range item["Media"].([]any) {
					media = append(media, &types.ForumMedia{
						Uid: int64(m.(map[string]any)["Uid"].(float64)),
						Id:  int64(m.(map[string]any)["Id"].(float64)),
						Url: m.(map[string]any)["Url"].(string),
					})
				}
			}
			var reviewCount int32
			var recommended bool
			if _, exist := item["Meta"]; exist {
				for _, m := range item["Meta"].([]any) {
					meta := m.(map[string]any)
					val := meta["Value"].(string)
					switch meta["Key"].(string) {
					case "_review_count":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							reviewCount = int32(i)
						}
					case "_recommended":
						recommended = true
					}
				}
			}
			var season []string
			var gender []string
			if _, exist := item["Season"]; exist {
				for _, m := range item["Season"].([]any) {
					meta := m.(map[string]any)
					switch meta["Key"].(string) {
					case "1":
						season = append(season, "春")
					case "2":
						season = append(season, "夏")
					case "3":
						season = append(season, "秋")
					case "4":
						season = append(season, "冬")
					}
				}
			}
			if _, exist := item["Gender"]; exist {
				for _, m := range item["Gender"].([]any) {
					meta := m.(map[string]any)
					switch meta["Key"].(string) {
					case "1":
						gender = append(gender, "中性香")
					case "2":
						gender = append(gender, "女香")
					case "3":
						gender = append(gender, "男香")
					}
				}
			}
			var brand []*types.PerfumeInfo
			var nose []*types.PerfumeInfo
			var group []*types.PerfumeInfo
			var topNote []*types.PerfumeInfo
			var heartNote []*types.PerfumeInfo
			var baseNote []*types.PerfumeInfo
			var perfume []*types.PerfumeInfo
			var version []*types.PerfumeInfo
			if _, exist := item["Brand"]; exist {
				for _, r := range item["Brand"].([]any) {
					relation := r.(map[string]any)
					brand = append(brand, &types.PerfumeInfo{
						Id:    int64(relation["Id"].(float64)),
						Title: relation["Title"].(string),
						Slug:  relation["Slug"].(string),
					})
				}
			}
			if _, exist := item["Nose"]; exist {
				for _, r := range item["Nose"].([]any) {
					relation := r.(map[string]any)
					nose = append(nose, &types.PerfumeInfo{
						Id:    int64(relation["Id"].(float64)),
						Title: relation["Title"].(string),
						Slug:  relation["Slug"].(string),
					})
				}
			}
			if _, exist := item["Group"]; exist {
				for _, r := range item["Group"].([]any) {
					relation := r.(map[string]any)
					group = append(group, &types.PerfumeInfo{
						Id:    int64(relation["Id"].(float64)),
						Title: relation["Title"].(string),
						Slug:  relation["Slug"].(string),
					})
				}
			}
			if _, exist := item["Note"]; exist {
				for _, r := range item["Note"].([]any) {
					relation := r.(map[string]any)
					groupId := int(relation["GroupId"].(float64))
					switch groupId {
					case 1:
						topNote = append(topNote, &types.PerfumeInfo{
							Id:    int64(relation["Id"].(float64)),
							Title: relation["Title"].(string),
							Slug:  relation["Slug"].(string),
						})
					case 2:
						heartNote = append(heartNote, &types.PerfumeInfo{
							Id:    int64(relation["Id"].(float64)),
							Title: relation["Title"].(string),
							Slug:  relation["Slug"].(string),
						})
					case 3:
						baseNote = append(baseNote, &types.PerfumeInfo{
							Id:    int64(relation["Id"].(float64)),
							Title: relation["Title"].(string),
							Slug:  relation["Slug"].(string),
						})
					}
				}
			}
			if _, exist := item["Relations"]; exist {
				for _, r := range item["Relations"].([]any) {
					relation := r.(map[string]any)
					section := relation["Section"].(string)
					switch section {
					case "perfume":
						perfume = append(perfume, &types.PerfumeInfo{
							Id:    int64(relation["Id"].(float64)),
							Title: relation["Title"].(string),
							Slug:  relation["Slug"].(string),
						})
					case "version":
						version = append(version, &types.PerfumeInfo{
							Id:    int64(relation["Id"].(float64)),
							Title: relation["Title"].(string),
							Slug:  relation["Slug"].(string),
						})
					}
				}
			}

			var id int64
			if _, exist := item["Id"]; exist {
				id = int64(item["Id"].(float64))
			}
			var title string
			if _, exist := item["Title"]; exist {
				title = item["Title"].(string)
			}
			var slug string
			if _, exist := item["Slug"]; exist {
				slug = item["Slug"].(string)
			}
			var year string
			if _, exist := item["Year"]; exist {
				year = item["Year"].(string)
			}
			var averageRating string
			if _, exist := item["Rating"]; exist {
				averageRating = moneyx.ParseStar(item["Rating"].(string), 1)
			}
			list = append(list, &types.PerfumeInfo{
				Id:            id,
				Title:         title,
				Slug:          slug,
				Year:          year,
				ReviewCount:   reviewCount,
				AverageRating: averageRating,
				Recommended:   recommended,
				Section:       sections,
				Media:         media,
				Brand:         brand,
				Nose:          nose,
				Group:         group,
				TopNote:       topNote,
				HeartNote:     heartNote,
				BaseNote:      baseNote,
				Perfume:       perfume,
				Version:       version,
				Season:        strings.Join(season, " "),
				Gender:        strings.Join(gender, " "),
			})
		}

		var hasNextPage bool
		if page*pageSize < total {
			hasNextPage = true
		}

		return &types.PerfumeFilterResp{
			Current:     page,
			PageSize:    pageSize,
			List:        list,
			HasNextPage: hasNextPage,
		}, nil
	}

	var aggs map[string]any
	filter = strings.ToLower(filter)
	switch filter {
	case "brand":
		var terms map[string]any
		if len(prefix) == 1 {
			mustConditions = append(mustConditions, map[string]any{
				"term": map[string]any{
					"Brand.Url": prefix,
				},
			})
			terms = map[string]any{
				"field": "Brand.Link",
				"include": ".*\\~" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else if len(prefix) > 1 {
			terms = map[string]any{
				"field": "Brand.Link",
				"include": ".*" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else {
			terms = map[string]any{
				"field": "Brand.Link",
				"size": page*pageSize*10,
			}
		}
		aggs = map[string]any{
			"group_by_brand": map[string]any{
				"terms": terms,
				"aggs": map[string]any{
					"page": map[string]any{
						"bucket_sort": map[string]any{
							"sort": map[string]any{},
							"from": (page-1)*pageSize*10,
							"size": pageSize*10,
						},
					},
				},
			},
		}
	case "nose":
		var terms map[string]any
		if len(prefix) == 1 {
			mustConditions = append(mustConditions, map[string]any{
				"term": map[string]any{
					"Nose.Url": prefix,
				},
			})
			terms = map[string]any{
				"field": "Nose.Link",
				"include": ".*\\~" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else if len(prefix) > 1 {
			terms = map[string]any{
				"field": "Nose.Link",
				"include": ".*" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else {
			terms = map[string]any{
				"field": "Nose.Link",
				"size": page*pageSize*10,
			}
		}
		aggs = map[string]any{
			"group_by_nose": map[string]any{
				"terms": terms,
				"aggs": map[string]any{
					"page": map[string]any{
						"bucket_sort": map[string]any{
							"sort": map[string]any{},
							"from": (page-1)*pageSize*10,
							"size": pageSize * 10,
						},
					},
				},
			},
		}
	case "group":
		var terms map[string]any
		if len(prefix) == 1 {
			mustConditions = append(mustConditions, map[string]any{
				"term": map[string]any{
					"Group.Url": prefix,
				},
			})
			terms = map[string]any{
				"field": "Group.Link",
				"include": ".*\\~" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else if len(prefix) > 1 {
			terms = map[string]any{
				"field": "Group.Link",
				"include": ".*" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else {
			terms = map[string]any{
				"field": "Group.Link",
				"size": page*pageSize*10,
			}
		}
		aggs = map[string]any{
			"group_by_group": map[string]any{
				"terms": terms,
				"aggs": map[string]any{
					"page": map[string]any{
						"bucket_sort": map[string]any{
							"sort": map[string]any{},
							"from": (page-1)*pageSize*10,
							"size": pageSize * 10,
						},
					},
				},
			},
		}
	case "note":
		var terms map[string]any
		if len(prefix) == 1 {
			mustConditions = append(mustConditions, map[string]any{
				"term": map[string]any{
					"Note.Url": prefix,
				},
			})
			terms = map[string]any{
				"field": "Note.Link",
				"include": ".*\\~" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else if len(prefix) > 1 {
			terms = map[string]any{
				"field": "Note.Link",
				"include": ".*" + prefix + ".*",
				"size": page*pageSize*10,
			}
		} else {
			terms = map[string]any{
				"field": "Note.Link",
				"size": page*pageSize*10,
			}
		}
		aggs = map[string]any{
			"group_by_note": map[string]any{
				"terms": terms,
				"aggs": map[string]any{
					"page": map[string]any{
						"bucket_sort": map[string]any{
							"sort": map[string]any{},
							"from": (page-1)*pageSize*10,
							"size": pageSize * 10,
						},
					},
				},
			},
		}
	case "season":
		aggs = map[string]any{
			"group_by_season": map[string]any{
				"terms": map[string]any{
					"field": "Season.Value",
					"size": page*pageSize*10,
				},
				"aggs": map[string]any{
					"page": map[string]any{
						"bucket_sort": map[string]any{
							"sort": map[string]any{},
							"from": (page-1)*pageSize*10,
							"size": pageSize * 10,
						},
					},
				},
			},
		}
	case "gender":
		aggs = map[string]any{
			"group_by_gender": map[string]any{
				"terms": map[string]any{
					"field": "Gender.Value",
					"size": page*pageSize*10,
				},
				"aggs": map[string]any{
					"page": map[string]any{
						"bucket_sort": map[string]any{
							"sort": map[string]any{},
							"from": (page-1)*pageSize*10,
							"size": pageSize * 10,
						},
					},
				},
			},
		}
	default:
		aggs = map[string]any{}
	}

	query = map[string]any{
		"size": 0,
		"query": map[string]any{
			"bool": map[string]any{
				"must": mustConditions,
			},
		},
		"aggs": aggs,
	}
	// logrus.Info(fmt.Sprintf("PerfumeFilter query: %+v", query))

	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		logrus.Info(fmt.Sprintf("PerfumeFilter Error encoding query: %+v", err))
		return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
	}

	// Perform the search request.
	client := l.svcCtx.EsClient
	res, err := client.Search(
		client.Search.WithContext(l.ctx),
		client.Search.WithIndex("scentrobe-kb-entry"),
		client.Search.WithBody(&buf),
		client.Search.WithTrackTotalHits(true),
		client.Search.WithPretty(),
	)
	if err != nil {
		logrus.Info(fmt.Sprintf("Error getting response: %+v", err))
		return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
	}

	defer res.Body.Close()
	if res.IsError() {
		var e map[string]any
		if err = json.NewDecoder(res.Body).Decode(&e); err != nil {
			logrus.Info(fmt.Sprintf("Error parsing the response body: %+v", err))
		} else {
			// Print the response status and error information.
			logrus.Info(fmt.Sprintf(
				"[%s] %s: %s",
				res.Status(),
				e["error"].(map[string]any)["type"],
				e["error"].(map[string]any)["reason"],
			))
		}

		return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
	}

	// logrus.Info(fmt.Sprintf("PerfumeFilter res: %+v", res))

	var r map[string]any
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		logrus.Info(fmt.Sprintf("Error parsing the response body: %+v", err))
		return nil, resultx.NewErrCode(resultx.SERVER_COMMON_ERROR)
	}

	// Print the response status, number of results, and request duration.
	// logrus.Info(fmt.Sprintf(
	// 	"[%s] %d hits; took: %dms",
	// 	res.Status(),
	// 	int(r["hits"].(map[string]any)["total"].(map[string]any)["value"].(float64)),
	// 	int(r["took"].(float64)),
	// ))

	// Print the ID and document source for each hit.
	// for _, hit := range r["hits"].(map[string]any)["hits"].([]any) {
	// 	logrus.Info(fmt.Sprintf(" * ID=%s, %s", hit.(map[string]any)["_id"], hit.(map[string]any)["_source"]))
	// }

	// logrus.Info(fmt.Sprintf("PerfumeFilter aggregations: %+v", r))

	var filterList []*types.ForumMeta
	if _, exist := r["aggregations"]; exist {
		for _, aggregation := range r["aggregations"].(map[string]any) {
			item := aggregation.(map[string]any)
			if _, ok := item["buckets"]; ok {
				for _, bucket := range item["buckets"].([]any) {
					parts := strings.Split(bucket.(map[string]any)["key"].(string), "~~~")
					var iId int64
					var iLabel string
					var iSlug string
					if len(parts) == 3 {
						if i, err := strconv.ParseInt(parts[2], 10, 64); err == nil {
							iId = i
						}
						iLabel = parts[0]
						iSlug = parts[1]
					}
					filterList = append(filterList, &types.ForumMeta{
						Id:    iId,
						Key:   iLabel,
						Label: iLabel,
						Slug:  iSlug,
						Value: strconv.FormatFloat(bucket.(map[string]any)["doc_count"].(float64), 'f', 0, 64),
					})
				}
			}
		}
	}

	return &types.PerfumeFilterResp{
		Current:    page,
		PageSize:   pageSize,
		FilterList: filterList,
	}, nil
}
